﻿#include "DASystemInfo.h"
#include "SysInfo/SysInfo.h"
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include <QtMath>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QRect>
#include <QDebug>
#include <QPen>
#include <QRegularExpression>
#include <QtCharts>

#if WIN32
    #include <iphlpapi.h>
    #include <iprtrmib.h>
    #include <winsock.h>
#endif
#include <QString>

#define CHARTMODEL

DASystemInfo::DASystemInfo(QWidget *parent)
    : QWidget{parent}
{
    SysInfo::instance().init();
    timer = new QTimer(this);
    LabelMem.setParent(this);
    LabelMem.setObjectName("LabelMem");
    LabelCPU.setParent(this);
    LabelCPU.setObjectName("LabelCPU");
    MemRate.setParent(this);
    MemRate.setObjectName("MemRate");
    CPURate.setParent(this);
    CPURate.setObjectName("CPURate");
    LabelUpload.setParent(this);
    LabelUpload.setObjectName("LabelUpload");
    LabelDownload.setParent(this);
    LabelDownload.setObjectName("LabelDownload");
    UploadRate.setParent(this);
    DownloadRate.setParent(this);
#ifdef CHARTMODEL
    ChartModel();
#else 
    StringModel();
#endif
}

void DASystemInfo::paintEvent(QPaintEvent *e)
{
    //创建绘画对象
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // 反锯齿;
    painter.setBrush(QBrush(QColor(0, 0, 0, 50)));
    painter.setPen(Qt::transparent);

    QRect rect = this->rect();
    rect.setWidth(rect.width()-1);
    rect.setHeight(rect.height()-1);
    painter.drawRoundedRect(rect,5,5);

//    QPen pen;
//    pen.setColor(Qt::red);
//    pen.setWidth(0);
//    painter.setPen(pen);
//    painter.drawRect(this->rect());
//    painter.end();
    QWidget::paintEvent(e);
}
double DASystemInfo::getSysInfoCpuRate()
{
    return SysInfo::instance().cpuLoadAverage();
}
QString DASystemInfo::getCpuUsed()
{
    double res = getSysInfoCpuRate();
    if ( res < 50.0)
    {
        CPURate.setStyleSheet("QLabel{ color:rgb(255,255,255)}");
    }
    if (res > 50.0)
    {
        CPURate.setStyleSheet("QLabel{ color:rgb(255,215,0)}");
    }
    if (res > 80.0)
    {
        CPURate.setStyleSheet("QLabel{ color:rgb(255,0,0)}");
    }

    QString result= QString("%1%").arg(res, 0, 'f', 2, QLatin1Char(' '));
    return result;
}
double DASystemInfo::getSysInfoMemRate()
{
    return SysInfo::instance().memoryUsed();
}
QString DASystemInfo::getMemUsed()
{
    double res = getSysInfoMemRate();
    if (res < 50)
    {
        MemRate.setStyleSheet("QLabel{ color:rgb(255,255,255)}");
    }
    if (res > 50)
    {
        MemRate.setStyleSheet("QLabel{ color:rgb(255,215,0)}");
    }
    if (res > 80)
    {
        MemRate.setStyleSheet("QLabel{ color:rgb(255,0,0)}");
    }
    QString result = QString("%1%").arg(res, 0, 'f', 2, QLatin1Char(' '));
    return result;
}

QString DASystemInfo::setRateUnitSensitive(DASystemInfo::RateUnit unit, DASystemInfo::Sensitive sensitive)
{
    switch (sensitive)
    {
        case Sensitive::Default:
        {
            switch (unit)
            {
                case RateUnit::RateBit:
                    return QString("b/s");
                case RateUnit::RateByte:
                    return QString("B/s");
                case RateUnit::RateKb:
                    return QString("Kb/s");
                case RateUnit::RateMb:
                    return QString("Mb/s");
                case RateUnit::RateGb:
                    return QString("Gb/s");
                case RateUnit::RateTb:
                    return QString("Tb/s");
                default:
                    //            qDebug()<<QString("Sensitive::Default,  RateUnit is RateUnknow.");
                    return QString("");
            }
        }
        case Sensitive::Upper:
        {
            switch (unit)
            {
                case RateUnit::RateBit:
                    return QString("BIT/S");
                case RateUnit::RateByte:
                    return QString("B/S");
                case RateUnit::RateKb:
                    return QString("KB/S");
                case RateUnit::RateMb:
                    return QString("MB/S");
                case RateUnit::RateGb:
                    return QString("GB/S");
                case RateUnit::RateTb:
                    return QString("TB/S");
                default:
                    //            qDebug()<<QString("Sensitive::Upper,  RateUnit is RateUnknow.");
                    return QString("");
            }
        }
        case Sensitive::Lower:
        {
            switch (unit)
            {
                case RateUnit::RateBit:
                    return QString("bit/s");
                case RateUnit::RateByte:
                    return QString("b/s");
                case RateUnit::RateKb:
                    return QString("kb/s");
                case RateUnit::RateMb:
                    return QString("mb/s");
                case RateUnit::RateGb:
                    return QString("gb/s");
                case RateUnit::RateTb:
                    return QString("tb/s");
                default:
                    //            qDebug()<<QString("Sensitive::Lower,  RateUnit is RateUnknow.");
                    return QString("");
            }
        }
        default:
        {
            //        qDebug()<<QString("Sensitive is RateUnknow.");
            return QString("");
        }
    }
}

double DASystemInfo::autoRateUnits(long speed, DASystemInfo::RateUnit &unit)
{
    /* 自动判断合适的速率单位,默认传进来的是 Byte
     * bit    0 ~ 7 位 (不到 1 字节)
     * Byte   1    ~ 2^10  Byte
     * KB     2^10 ~ 2^20  Byte
     * MB     2^20 ~ 2^30  Byte
     * GB     2^30 ~ 2^40  Byte
     * TB     2^40 ~ 2^50  Byte
     */

    if (unit != DASystemInfo::RateByte)
    {
        //        qDebug()<<"请先将单位转为字节(byte)后再传参";
        return -1;
    }

    double sp = 0;
    if (0 <= speed && speed < qPow(2, 10))
    {
        unit = DASystemInfo::RateByte;
        sp = speed;
    }
    else if (qPow(2, 10) <= speed && speed < qPow(2, 20))
    {
        unit = DASystemInfo::RateKb;
        sp = static_cast<double>(speed / qPow(2, 10) * 1.0);
    }
    else if (qPow(2, 20) <= speed && speed < qPow(2, 30))
    {
        unit = DASystemInfo::RateMb;
        sp = static_cast<double>(speed / qPow(2, 20) * 1.0);
    }
    else if (qPow(2, 30) <= speed && speed < qPow(2, 40))
    {
        unit = DASystemInfo::RateGb;
        sp = static_cast<double>(speed / qPow(2, 30) * 1.0);
    }
    else if (qPow(2, 40) <= speed && speed < qPow(2, 50))
    {
        unit = DASystemInfo::RateTb;
        sp = static_cast<double>(speed / qPow(2, 40) * 1.0);
    }
    else {
        unit = DASystemInfo::RateUnknow;
        //        qDebug()<<"本设备网络速率单位传输超过 TB, 或者低于 0 Byte.";
        sp = -1;
    }

    return sp;
}

void DASystemInfo::getNetUploadDownload()
{
#if defined(Q_OS_WIN)
    QString NetSpeedInfo;
    PMIB_IFTABLE    m_pTable = nullptr;
    DWORD    m_dwAdapters = 0;
    ULONG    uRetCode = GetIfTable(m_pTable, &m_dwAdapters, TRUE);
    if (uRetCode == ERROR_NOT_SUPPORTED)
    {
        //return (-1);
        return;
    }

    if (uRetCode == ERROR_INSUFFICIENT_BUFFER)
    {
        m_pTable = (PMIB_IFTABLE)new BYTE[65535];   //假设端口数不超过65535个
    }

    DWORD   dwLastIn = 0;           //上一秒钟的接收字节数
    DWORD   dwLastOut = 0;          //上一秒钟的发送字节数
    DWORD   dwBandIn = 0;           //下载速度
    DWORD   dwBandOut = 0;          //上传速度

    NetSpeedInfo.clear();
    GetIfTable(m_pTable, &m_dwAdapters, TRUE);
    DWORD   dwInOctets = 0;
    DWORD   dwOutOctets = 0;

    //将所有端口的流量进行统计
    for (UINT i = 0; i < m_pTable->dwNumEntries; i++)
    {
        MIB_IFROW   Row = m_pTable->table[i];
        dwInOctets += Row.dwInOctets;
        dwOutOctets += Row.dwOutOctets;
    }

    dwBandIn = dwInOctets - dwLastIn;       //下载速度
    dwBandOut = dwOutOctets - dwLastOut;    //上床速速
    if (dwLastIn <= 0)
    {
        dwBandIn = 0;
    }
    else
    {
        dwBandIn = dwBandIn / 1024; //b转换成kb
    }

    if (dwLastOut <= 0)
    {
        dwBandOut = 0;
    }
    else
    {
        dwBandOut = dwBandOut / 1024;   //b转换成kb
    }

    dwLastIn = dwInOctets;
    dwLastOut = dwOutOctets;

    //        printf("收到字节: %u bytes\n", dwLastIn);
    //        printf("发送字节: %u bytes\n", dwLastOut);
    //        printf("下行速度: %u KB\n", dwBandIn);
    //        printf("上行速度: %u KB\n", dwBandOut);
    //        printf("--------------------------\n");
    NetSpeedInfo = QString("收到字节: %1 bytes\r\n"
                           "发送字节： %2 bytes\r\n"
                           "下行速度: %3 Kb\r\n"
                           "上行速度: %4 kb\r\n")
                       .arg(dwLastIn)
                       .arg(dwLastOut)
                       .arg(dwBandIn)
                       .arg(dwBandOut);
    qDebug()<<NetSpeedInfo;
    delete [] m_pTable;
#elif defined(Q_OS_LINUX)
    long uploadAll = 0;
    long downloadAll = 0;

    long upload = 0;
    long download = 0;

    QFile file("/proc/net/dev");
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream stream(&file);
    QString line = stream.readLine();
    line  = stream.readLine();
    while (!line.isNull()) {
        line = line.trimmed();
        QStringList list = line.split(QRegularExpression("\\s{1,}"));   // 匹配任意 大于等于1个的 空白字符

        if (!list.isEmpty()) {
            upload = list.at(9).toLong();
            download = list.at(1).toLong();
        }

        uploadAll += upload;
        downloadAll += download;
        line  = stream.readLine();
    }
    RateUnit upunit = RateByte;
    RateUnit dounit = RateByte;
    Sensitive  sensitive = Upper;
    double upspeed = autoRateUnits((uploadAll - old_upload) / (timer->interval() / 1000), upunit);
    double dospeed = autoRateUnits((downloadAll - old_download) / (timer->interval() / 1000), dounit);

    UploadRate.setText(QString("%1").arg(upspeed, 0, 'f', 2, QLatin1Char(' ')) + setRateUnitSensitive(upunit,sensitive));
    DownloadRate.setText(QString("%1").arg(dospeed, 0, 'f', 2, QLatin1Char(' ')) + setRateUnitSensitive(dounit,sensitive));

    old_upload = uploadAll;
    old_download = downloadAll;
#endif
}

void DASystemInfo::StringModel()
{
#if 1
    LabelMem.setText("MEM:");
    LabelMem.setFixedWidth(50);
    LabelCPU.setText("CPU:");
    LabelCPU.setFixedWidth(50);
    MemRate.setFixedWidth(80);
    CPURate.setFixedWidth(80);
    LabelUpload.setText("↑:");
    LabelUpload.setAlignment(Qt::AlignCenter);
    LabelUpload.setFixedWidth(50);
    LabelDownload.setText("↓:");
    LabelDownload.setAlignment(Qt::AlignCenter);
    LabelDownload.setFixedWidth(50);
    UploadRate.setObjectName("UploadRate");
    DownloadRate.setObjectName("DownloadRate");
    connect(timer,&QTimer::timeout,[=](){
        CPURate.setText(getCpuUsed());
        MemRate.setText(getMemUsed());
#if defined(Q_OS_UNIX)
        getNetUploadDownload();
#endif
    });
    timer->start(1000);

    QHBoxLayout *hbl1 =new QHBoxLayout();
    hbl1->addWidget(&LabelMem);
    hbl1->addWidget(&MemRate);
    hbl1->addWidget(&LabelUpload);
    hbl1->addWidget(&UploadRate);
    hbl1->setSpacing(0);
    QHBoxLayout *hbl2 =new QHBoxLayout();
    hbl2->addWidget(&LabelCPU);
    hbl2->addWidget(&CPURate);
    hbl2->addWidget(&LabelDownload);
    hbl2->addWidget(&DownloadRate);
    hbl2->setSpacing(0);
    QVBoxLayout *vbl =new QVBoxLayout();
    vbl->addLayout(hbl1);
    vbl->addLayout(hbl2);
    setLayout(vbl);
#endif

}

void DASystemInfo::ChartModel()
{
    LabelMem.setText("内\n存");
    LabelMem.setFixedWidth(15);
    LabelCPU.setText("处\n理\n器");
    LabelCPU.setFixedWidth(15);

#if CPULineSeries
    QValueAxis *axisX = new QValueAxis();
    QValueAxis *axisY = new QValueAxis();
    axisX->setRange(0, 50);
    axisX->setVisible(false);
    axisY->setRange(0, 100);
    cSeries = new QLineSeries(this);
    cChartView = new QChartView(this);
    cChartView->setFixedWidth(80);
    cSeries->attachAxis(axisX);
    cSeries->attachAxis(axisY);
#else
    cSeries = new QPieSeries(this);
    cChartView = new QChartView(this);
    cChartView->setFixedWidth(80);
    cRate = new QLabel(cChartView);
    cRate->setFixedSize(40,40);
    cRate->setStyleSheet("color:rgb(255,255,255);");
    cRate->setAlignment(Qt::AlignCenter);
    cRate->move(20,20);
    cSeries->setHoleSize(0.5);
    cSeries->setPieSize(1);
#endif

    mSeries = new QPieSeries(this);
    mChartView = new QChartView(this);
    mChartView->setFixedWidth(80);
    mRate = new QLabel(mChartView);
    mRate->setFixedSize(40,40);
    mRate->setStyleSheet("color:rgb(255,255,255);");
    mRate->setAlignment(Qt::AlignCenter);
    mRate->move(20,20);
    mSeries->setHoleSize(0.5);
    mSeries->setPieSize(1);
    



    connect(timer,&QTimer::timeout,[=]()
    {
        double res1 = getSysInfoMemRate();
        double res2 = getSysInfoCpuRate();
        QString mresult= QString("%1%").arg(res1, 0, 'f', 0, QLatin1Char(' '));
        QString cresult= QString("%1%").arg(res2, 0, 'f', 0, QLatin1Char(' '));
        MemChart(res1);
        CpuChart(res2);
#if !CPULineSeries   
        cRate->setText(cresult);
        QPieSlice* cslice0 = cSeries->slices().at(0);
        cslice0->setPen(QPen(QColor(220,20,60), 0));  //设置该切片的pen属性, 即绘制该切片边框的钢笔
        cslice0->setBrush(QBrush(QColor(220,20,60))); //设置该切片的brush属性, 即填充切片的画笔颜色
        QPieSlice* cslice1 = cSeries->slices().at(1);
        cslice1->setPen(QPen(QColor(0,0,128), 0));  //设置该切片的pen属性, 即绘制该切片边框的钢笔
        cslice1->setBrush(QBrush(QColor(0,0,128))); //设置该切片的brush属性, 即填充切片的画笔颜色
#endif
        mRate->setText(mresult);
        QPieSlice* mslice0 = mSeries->slices().at(0);
        mslice0->setPen(QPen(QColor(220,20,60), 0));  //设置该切片的pen属性, 即绘制该切片边框的钢笔
        mslice0->setBrush(QBrush(QColor(220,20,60))); //设置该切片的brush属性, 即填充切片的画笔颜色
        QPieSlice* mslice1 = mSeries->slices().at(1);
        mslice1->setPen(QPen(QColor(0,0,128), 0));  //设置该切片的pen属性, 即绘制该切片边框的钢笔
        mslice1->setBrush(QBrush(QColor(0,0,128))); //设置该切片的brush属性, 即填充切片的画笔颜色
        
        
    });
    timer->start(1000);

    {
        mChartView->setObjectName("mChartView");
        mChartView->setRenderHint(QPainter::Antialiasing);
        mChartView->setPalette(QPalette(QColor(0, 0, 0, 0)));
        mChartView->setContentsMargins(0,0,0,0);
        mChartView->chart()->addSeries(mSeries);
        mChartView->chart()->setBackgroundVisible(false);
        mChartView->chart()->legend()->setVisible(false);
        mChartView->chart()->setMargins(QMargins(0,0,0,0));
    }
#if CPULineSeries
    {
        cChartView->setObjectName("cChartView");
        cChartView->setRenderHint(QPainter::Antialiasing);
        cChartView->setPalette(QPalette(QColor(0, 0, 0, 0)));
        cChartView->setContentsMargins(0,0,0,0);
        cChartView->chart()->addSeries(cSeries);
        cChartView->chart()->addAxis(axisX, Qt::AlignBottom);
        cChartView->chart()->addAxis(axisY, Qt::AlignLeft);
        cChartView->chart()->setBackgroundVisible(false);
        cChartView->chart()->legend()->setVisible(false);
        cChartView->chart()->setMargins(QMargins(0,0,0,0));
    }
#else
    {
        cChartView->setObjectName("cChartView");
        cChartView->setRenderHint(QPainter::Antialiasing);
        cChartView->setPalette(QPalette(QColor(0, 0, 0, 0)));
        cChartView->setContentsMargins(0,0,0,0);
        cChartView->chart()->addSeries(cSeries);
        cChartView->chart()->setBackgroundVisible(false);
        cChartView->chart()->legend()->setVisible(false);
        cChartView->chart()->setMargins(QMargins(0,0,0,0));
    }
#endif
    QHBoxLayout *hbl1 =new QHBoxLayout();
    hbl1->addWidget(&LabelMem);
    hbl1->addWidget(mChartView);
    hbl1->addWidget(&LabelCPU);
#if CPULineSeries
    hbl1->addWidget(cChartView);
#else
    hbl1->addWidget(cChartView);
#endif
    hbl1->setSpacing(0);
    hbl1->setContentsMargins(0,0,0,0);

    QGridLayout *hb =new QGridLayout();
    hb->addLayout(hbl1,0,0);
    hb->setSpacing(0);
    hb->setContentsMargins(0,0,0,0);
    setLayout(hb);
}
void DASystemInfo::MemChart(double memoryUsed)
{
    mSeries->clear();
    mSeries->append("Used", memoryUsed);
    mSeries->append("Free", 100.0 - memoryUsed);
}

void DASystemInfo::CpuChart(double cpuLoadAverage)
{
#if  CPULineSeries
    cSeries->append(mPointPositionX++, cpuLoadAverage);
    //超出显示范围之后往前滚动一个格子
    if (cSeries->count() > 50) {
        QChart* chart = cChartView->chart();
        chart->scroll(chart->plotArea().width() / 50, 0);
    }
#else
    cSeries->clear();
    cSeries->append("Used", cpuLoadAverage);
    cSeries->append("Free", 100.0 - cpuLoadAverage);
#endif
}
